Skip to content

Conversation

JoshuaBehrens
Copy link
Contributor

@JoshuaBehrens JoshuaBehrens commented Aug 11, 2025

This commit bricks some code flows. Other changes needs to be done as well. See #300

Q A
Bug fix? "yes"
New feature? no
Docs? no
Issues Fix #114
License MIT

In other issues and pull requests the model class should be used as a simple struct, only holding scalar values and should not have any business logic effect by inheritance. This has been the case for embeddings, completions and other API calls like Whisper audio, and Dall-e images. Right now this makes it difficult to share different instances of the model class with various providers, although a lot of providers share the same models, and therefore likely the same capabilities. This eventually will reduce the amount of code you have to write to establish new providers that replicate existing API's with a well-known subset of models or add better support for custom models like OpenAI fine-tuned models or ollamas self-built models.

To stop this, I have changed all instanceof checks matching a specific model subclass to refer to the expected input or output capabilities of the model, that were implemented in the specific messagenormalizer, resultconverter or modelclient. It was sometimes not easy to decide, which approach to go as not every input has a corresponding output capability. Some are merged behind a helpful name like tool calling or but when you are not familiar with the underlying decisions the name input_multiple is not easily relatable to a vector calculations done by embeddings features. So either this could need a refreshing rename, new capability or some documentation around it on the capability enum.

…ring pre-request situations to reduce dependency on specific model class
…ace during pre-request situations to reduce dependency on specific model class
…e during response situations to reduce dependency on specific model class
@JoshuaBehrens JoshuaBehrens marked this pull request as ready for review August 11, 2025 03:15
@carsonbot carsonbot changed the title Use capabilities instead of instanceof checks Use capabilities instead of instanceof checks Aug 11, 2025
@chr-hertel
Copy link
Member

Hey @JoshuaBehrens, I think you are on a good topic here, but we def need to align before we roll out changes like that - also looking at #300.

I think as well that we'll need some changes on how the Platform works, and I was working on #136 to bring in a new mechanism for result conversion and reducing the repetitive implementations. And I def need to wrap that up before we gonna move on Capabilities/Actions/Tasks/etc.

@JoshuaBehrens
Copy link
Contributor Author

@chr-hertel fine to me :) I admit I expected it to start a discussion instead of an LGTM merge.

I also assume, that we gonna need some model metadata to check before invoking it. Some instanceof check sort-of promised existence of the model which now is a lot of trust towards the user. And in ollama we already have some checks about embeddings and somewhere else is a listModels function. So there is also need for that :)

Depending on the way "we need to align" I am not sure whether Github issue commentary is a helpful chat alternative

@OskarStark OskarStark added the Platform Issues & PRs about the AI Platform component label Aug 13, 2025
@carsonbot carsonbot changed the title Use capabilities instead of instanceof checks [Platform] Use capabilities instead of instanceof checks Aug 13, 2025
public function supports(Model $model): bool
{
return $model instanceof Gemini;
return $model->supports(Capability::INPUT_MESSAGES);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way those model are used in the request method is

$url = \sprintf(
            'https://generativelanguage.googleapis.com/v1beta/models/%s:%s',
            $model->getName(),
            $options['stream'] ?? false ? 'streamGenerateContent' : 'generateContent',
        );

Which means that the name should be a valid model name for Gemini.

While it's kinda implicit when using a Gemini Model (and should be enforced by phpdoc), it's not the cade for a generic model...

So I dunno if you should have something like

return $model->supports(Capability::INPUT_MESSAGES)
    && \in_array($model->getName(), Gemini::MODELS, true)

Same for others supports methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intended with my comment on

This eventually will reduce the amount of code you have to write to establish new providers that replicate existing API's with a well-known subset of models or add better support for custom models like OpenAI fine-tuned models or ollamas self-built models.

I am with you, that it would be great if you know in before whether the model exists but the API can tell with a 404. No need to update the library to support new models.

@ohader
Copy link

ohader commented Sep 18, 2025

@JoshuaBehrens Thanks for pointing me to this PR. I tried to come up with an alternative approach that introduces a new PHP attribute like AsModel(platform: 'ollama') to connect models to model-clients.

For instance, this would allow to create a custom OliverHader\AI\OllamaModel (which uses the AsModel attribute) that still works with Symfony\AI\Platform\Bridge\Ollama\OllamaClient. This way, I could adjust the capabilities of the model individually, e.g. by a discovery service invoking the remote platform, fetch all available models and their capabilities).

What do you think?

ohader@dbd20d4

@OskarStark
Copy link
Contributor

What about #[AsModelClient(platform: 'ollama', capabilities: ['tool-calling', 'streaming'])] and the we could maintain a list of models with their capabilities in a ModelCatalog which could get infos from an API.

Ofc we need to make sure, that you can only register one ModelClient per platform and capability.

@ohader
Copy link

ohader commented Sep 19, 2025

What about #[AsModelClient(platform: 'ollama', capabilities: ['tool-calling', 'streaming'])] and the we could maintain a list of models with their capabilities in a ModelCatalog which could get infos from an API.

I like the idea, probably some more attributes might he helpful (I'd like to play around with that in some demo code to get a better feeling).

What would ModelCatalog offer from your point of view?
Does it offer a specific \Symfony\AI\Platform\Model implementation?
Or would if offer the model names with their capabilities, e.g. ['llama3.2:latest' => ['input-messages', 'output-text', 'output-structured', 'tool-calling'], 'gpt-oss:latest' => [...]]?

In general I'm seeking for a way to resolve the (current) strict coupling of components by using attributes. An in addition, having a way to discover the "feature set" (models, clients, capabilities) more easily with dependency injection (I see, that DI is out of scope for this lib, but it would be helpful for project integrations - introducing attributes/tags are just perfect for that).

@OskarStark
Copy link
Contributor

Basically yes, but this file should be updated by syncing or so, like we do for the ICU data for example Symfony

@JoshuaBehrens
Copy link
Contributor Author

@ohader I don't get the benefit of the attribute yet. It feels like you need a different way to configure model classes so one can mark existing model classes for new platforms. So how would one extend it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform Issues & PRs about the AI Platform component Status: Needs Review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Custom URL ability or generic bridges?
6 participants